﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Xml.Serialization;


namespace BDSim
{
    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class SatOrbit
    {
        #region 字段

        private Satellite _Parent;

        private OrbitPredictParamClass _COrbitPredictParam = new OrbitPredictParamClass();

        private IntegrationParamClass _IntegrationParam=new IntegrationParamClass();
        private SatPosType _SatPosType = SatPosType.J2000;
        private NodePosVelClass _InitPV_ECI = new NodePosVelClass()
        {
           // Epoch= new UTCTimeClass(),
             Epoch = BDSimObject.GlobalStartTime.ToUTCTimeClass(),
            x = 16983361.4247804990,
            y = -20353706.4413478, 
            z = -0.00155838616757,
            vx = 1709.450048999987,
            vy = 1426.384334400012,
            vz = 3179.607803900002
        };

        private NodePosVelClass _InitPV_ECF = new NodePosVelClass();
        
        private OrbitSixElementClass _SixOrbitElement=new OrbitSixElementClass();
        
        private OrbitPredict_Input _OrbitPredictInput=new OrbitPredict_Input();
        private OrbitPredict_Param _OrbitPredictParam=new OrbitPredict_Param();
        private OrbitPredict_Output _OrbitPredictOutput=new OrbitPredict_Output();
        [XmlIgnore]
        public SimParam SimParam;//系统时间，包括初始时间，系统运行步长、步数
        /// <summary>
        /// 实时轨道状态,ECI坐标
        /// </summary>
        [XmlIgnore]
        public NodePosVel RealECIPV = new NodePosVel();

        /// <summary>
        /// 实时轨道状态，ECF坐标
        /// </summary>
        [XmlIgnore]
        public NodePosVel RealECFPV = new NodePosVel();

        /// <summary>
        ///轨道后插表
        /// </summary>        
        [XmlIgnore]
        public BackDiff OrbBackDif;

        #endregion

        #region 属性

        /// <summary>
        /// 该轨道实例的父级，表示它属于哪颗卫星
        /// </summary>
        [XmlIgnore,Browsable(false)]
        public Satellite Parent
        {
            get { return _Parent; }
            set { _Parent = value; }
        }

        #region 力模型与积分器窗口设置相关属性

        /// <summary>
        /// 轨道力模型参数，界面配置
        /// </summary>      
        [PropertyOrder(1), Browsable(false)]
        public OrbitPredictParamClass COrbitPredictParam
        {
            get { return _COrbitPredictParam; }
            set { _COrbitPredictParam = value; }
        }

        /// <summary>
        /// 积分器参数，界面设置
        /// </summary>
        [Browsable(false)]
        public IntegrationParamClass IntegrationParam
        {
            get { return _IntegrationParam; }
            set { _IntegrationParam = value; }
        }             

      

        #endregion

        /// <summary>
        /// 卫星坐标类型,界面设置
        /// </summary>
        [PropertyOrder(2), DescriptionAttribute("初始轨道类型")]
        public SatPosType SatPosType
        {
            get { return _SatPosType; }
            set
            {
                InitPV_ECI.Epoch = BDSimObject.GlobalStartTime.ToUTCTimeClass();
                InitPV_ECF.Epoch = BDSimObject.GlobalStartTime.ToUTCTimeClass();
                SixOrbitElement.epoch = BDSimObject.GlobalStartTime.ToUTCTimeClass();
                switch (value)
                {

                    case SatPosType.CGCS2000:
                        {
                            PropertyFieldControl.SetPropertyVisibility(this, "InitPV_ECF", true);
                            PropertyFieldControl.SetPropertyVisibility(this, "SixOrbitElement", false);
                            PropertyFieldControl.SetPropertyVisibility(this, "InitPV_ECI", false);
                            switch (_SatPosType)
                            {
                                case SatPosType.J2000:
                                    InitPV_ECF = SatPVECI2ECF(InitPV_ECI);
                                    SixOrbitElement = PV2OrbitElement(InitPV_ECI);
                                    break;
                                case SatPosType.六根数:
                                    InitPV_ECI = OrbitElement2PV(SixOrbitElement);
                                    InitPV_ECF = SatPVECI2ECF(InitPV_ECI);
                                    break;
                                default:
                                    break;
                            }
                        }
                        break;
                    case SatPosType.J2000:
                        PropertyFieldControl.SetPropertyVisibility(this, "InitPV_ECI", true);
                        PropertyFieldControl.SetPropertyVisibility(this, "InitPV_ECF", false);
                        PropertyFieldControl.SetPropertyVisibility(this, "SixOrbitElement", false);
                        switch (_SatPosType)
                        {
                            case SatPosType.CGCS2000:
                                InitPV_ECI = SatPVECF2ECI(InitPV_ECF);
                                SixOrbitElement = PV2OrbitElement(InitPV_ECI);
                                break;
                            case SatPosType.六根数:
                                InitPV_ECI = OrbitElement2PV(SixOrbitElement);
                                InitPV_ECF = SatPVECI2ECF(InitPV_ECI);
                                break;
                            default:
                                break;
                        }
                        break;
                    case SatPosType.六根数:
                        PropertyFieldControl.SetPropertyVisibility(this, "SixOrbitElement", true);
                        PropertyFieldControl.SetPropertyVisibility(this, "InitPV_ECF", false);
                        PropertyFieldControl.SetPropertyVisibility(this, "InitPV_ECI", false);
                        switch (_SatPosType)
                        {
                            case SatPosType.CGCS2000:
                                InitPV_ECI = SatPVECF2ECI(InitPV_ECF);
                                SixOrbitElement = PV2OrbitElement(InitPV_ECI);
                                break;
                            case SatPosType.J2000:
                                SixOrbitElement = PV2OrbitElement(InitPV_ECI);
                                InitPV_ECF = SatPVECI2ECF(InitPV_ECI);
                                break;
                            default:
                                break;
                        }

                        break;
                    default:
                        break;
                }
                _SatPosType = value;
                ///根据界面选中的卫星坐标类型选择显示不同的坐标属性设置
                BDSimObject.DisplayObjectProperty(Parent);
            }
        }

        /// <summary>
        /// 卫星初始位置速度,界面设置
        /// </summary>
        [Browsable(true), CategoryAttribute("初始轨道（ECI）"), DescriptionAttribute("轨道初始参数（ECI）, 位置单位：米，速度单位：米/秒"), TypeConverter(typeof(PropertySorter)), PropertyOrder(3)]
        public NodePosVelClass InitPV_ECI
        {
            get { return _InitPV_ECI; }
            set { _InitPV_ECI = value; }
        }

        [Browsable(false), CategoryAttribute("初始轨道（ECF）"), DescriptionAttribute("轨道初始参数（ECF）, 位置单位：米，速度单位：米/秒"), TypeConverter(typeof(PropertySorter)), PropertyOrder(3)]
        public NodePosVelClass InitPV_ECF
        {
            get { return _InitPV_ECF; }
            set { _InitPV_ECF = value; }
        }


        /// <summary>
        /// 初始轨道六根数,界面设置
        /// </summary>
        [Browsable(false), CategoryAttribute("轨道六根数"), DescriptionAttribute("轨道初始六根数"), TypeConverter(typeof(PropertySorter)), PropertyOrder(3)]
        public OrbitSixElementClass SixOrbitElement
        {
            get { return _SixOrbitElement; }
            set { _SixOrbitElement = value; }
        }     


        #endregion
                
        #region 方法

        /// <summary>
        /// 轨道仿真初始化
        /// </summary>
        /// <returns></returns>
        public void Init()
        {
            SimParam = Parent.simParam;
            _OrbitPredictInput.startTime = 0;
            _OrbitPredictInput.endTime = 0;
            _OrbitPredictInput.step = _IntegrationParam.StepSize;
            _OrbitPredictParam = COrbitPredictParam.ConvertToStruct();
            if (COrbitPredictParam.PropagateType==EPropagateType.TwoBody)
            {               
                _OrbitPredictParam.m_bMoonPert = 0;
                _OrbitPredictParam.m_bNonSphPert = 0;
                _OrbitPredictParam.m_bSrpPert = 0;
                _OrbitPredictParam.m_bSunPert = 0;
                _OrbitPredictParam.m_rel = 0;
                _OrbitPredictParam.m_tide = 0;
            }
            ///根据当前的轨道坐标判断初始位置
            switch (SatPosType)
            {
                case SatPosType.CGCS2000:
                    InitPV_ECF.Epoch = Parent.simParam.pStartTime.ToUTCTimeClass();
                    InitPV_ECI = SatPVECF2ECI(InitPV_ECF);
                    SixOrbitElement = PV2OrbitElement(InitPV_ECI);
                    break;
                case SatPosType.J2000:
                    InitPV_ECI.Epoch = Parent.simParam.pStartTime.ToUTCTimeClass();
                    SixOrbitElement = PV2OrbitElement(InitPV_ECI);
                    InitPV_ECF = SatPVECI2ECF(InitPV_ECI);
                    break;
                case SatPosType.六根数:
                    SixOrbitElement.epoch = Parent.simParam.pStartTime.ToUTCTimeClass();
                    InitPV_ECI = OrbitElement2PV(SixOrbitElement);
                    InitPV_ECF = SatPVECI2ECF(InitPV_ECI);
                    break;
                default:
                    break;
            }

            _OrbitPredictParam.m_orbit = InitPV_ECI.ConvertToStruct();//轨道初始位置输入必须是ECI坐标
            _OrbitPredictParam.m_orbit.epoch = Parent.simParam.pStartTime;
            _OrbitPredictParam.m_LeapSec = ReadEOP.EOP_GetLeapSec(Parent.simParam.pStartTime)+19;

            int flag = CSatelliteDLL.OrbitPredict_init(_OrbitPredictInput, _OrbitPredictParam, out _OrbitPredictOutput);

            _OrbitPredictInput.presatdf = _OrbitPredictOutput.satdf;
            OrbBackDif = _OrbitPredictOutput.satdf;
            RealOut(_OrbitPredictOutput.satdf);
        }

        /// <summary>
        /// 轨道整步点仿真
        /// </summary>
        /// <returns></returns>
        public void FixedStepOut()
        {
            _OrbitPredictInput.endTime += _OrbitPredictInput.step;

            CSatelliteDLL.OrbitPredict_output(_OrbitPredictInput, out _OrbitPredictOutput);
            _OrbitPredictInput.presatdf = _OrbitPredictOutput.satdf;
            _OrbitPredictInput.startTime = _OrbitPredictInput.endTime;
            OrbBackDif = _OrbitPredictOutput.satdf;//为外部观测数据生成提供数据
        }

        /// <summary>
        /// 轨道实时仿真
        /// </summary>
        /// <returns></returns>
        public void RealOut(BackDiff orbBackDif)
        {
            double nowTime = SimParam.simClks * (((double)SimParam.simStepInt) * 1e-6);
            int m = 14;
            int n = 3;
            double trpTime = nowTime;//需要差值的时刻(相对时间，单位秒)
            RealECIPV = CSatelliteDLL.KSGTRP(m, n, trpTime, orbBackDif);
            UTCTime curTime = new UTCTime();

            curTime.IntJD = BDSimObject.GlobalStartTime.IntJD + Math.Floor(nowTime / 86400.0);
            curTime.DouJD = BDSimObject.GlobalStartTime.DouJD + nowTime / 86400.0 - Math.Floor(nowTime / 86400.0);
            curTime.JD = curTime.IntJD + curTime.DouJD;
            TimeSpaceDll.CAL2UTC(out curTime, 2);
            if (Math.Round(curTime.sec, 1) == 60)
            {
                curTime.sec = 0;
                curTime.min++;
            }
            RealECIPV.epoch = curTime;
            TimeSpaceDll.ECI2ECF(RealECIPV, 0, 0, out RealECFPV);
        }
        

        /// <summary>
        /// 位置速度转轨道根数
        /// </summary>
        /// <param name="pv"></param>
        /// <returns></returns>
        OrbitSixElementClass PV2OrbitElement(NodePosVelClass ECI_pv)
        {
            OrbitSixElementClass SixElementClass = new OrbitSixElementClass();
            NodePosVel ECIPV=new NodePosVel();
            Orbit6Element Ele=new Orbit6Element();
            ECIPV = ECI_pv.ConvertToStruct();
            TimeSpaceDll.ECI2Element(ECIPV, out Ele);
            SixElementClass = Ele.ConvertToClass();
            return SixElementClass;
        }

        /// <summary>
        /// 轨道根数转位置速度
        /// </summary>
        /// <param name="pv"></param>
        /// <returns></returns>
        NodePosVelClass OrbitElement2PV(OrbitSixElementClass ELe_6)
        {
            NodePosVelClass PosVelClass = new NodePosVelClass();
            NodePosVel ECIPV = new NodePosVel();
            Orbit6Element Ele = new Orbit6Element();
            Ele = ELe_6.ConvertToStruct();
            TimeSpaceDll.Element2ECI(Ele, out ECIPV);
            PosVelClass = ECIPV.ConvertToClass();
            return PosVelClass;
        }

        /// <summary>
        /// ECI坐标转ECF
        /// </summary>
        /// <param name="ECI_pv"></param>
        /// <returns></returns>
        NodePosVelClass SatPVECI2ECF(NodePosVelClass ECI_pv)
        {
            NodePosVelClass ECF_pv = new NodePosVelClass();
            NodePosVel ECIPos = new NodePosVel();
            NodePosVel ECFPos = new NodePosVel();
            ECIPos = ECI_pv.ConvertToStruct();
            TimeSpaceDll.CAL2UTC(out ECIPos.epoch, 1);//必须转成儒略日
            TimeSpaceDll.ECI2ECF(ECIPos, 0, 0, out ECFPos);
            ECF_pv = ECFPos.ConvertToClass();
            return ECF_pv;
        }

        /// <summary>
        /// ECF坐标转ECI
        /// </summary>
        /// <param name="ECF_pv"></param>
        /// <returns></returns>
        NodePosVelClass SatPVECF2ECI(NodePosVelClass ECF_pv)
        {
            NodePosVelClass ECI_pv = new NodePosVelClass();
            NodePosVel ECIPos = new NodePosVel();
            NodePosVel ECFPos = new NodePosVel();
            ECFPos = ECF_pv.ConvertToStruct();
            TimeSpaceDll.CAL2UTC(out ECFPos.epoch, 1);//必须转成儒略日
            TimeSpaceDll.ECF2ECI(ECFPos, 0, 0, out ECIPos);
            ECI_pv = ECIPos.ConvertToClass();
            return ECI_pv;
        }

        #region 识别卫星类型

        public enum enumSatType
        {
            /// <summary>
            /// MEO卫星
            /// </summary>
            MEO,

            /// <summary>
            /// GEO卫星
            /// </summary>
            GEO,

            /// <summary>
            /// IGSO卫星
            /// </summary>
            IGSO,

            /// <summary>
            /// LEO卫星
            /// </summary>
            LEO,

            /// <summary>
            /// HEO卫星
            /// </summary>
            HEO,
        }

        /// <summary>
        /// 识别卫星类型
        /// </summary>
        /// <param name="satPV">卫星轨道位置速度</param>
        /// <returns>卫星类型（IGSO,MEO,GEO,LEO,HEO）</returns>
        public static enumSatType GetSatType(NodePosVel satPV)
        {
            enumSatType satType = enumSatType.MEO;

            //计算轨道根数
            Orbit6Element orbitelem;
            TimeSpaceDll.ECI2Element(satPV, out orbitelem);

            //计算轨道高度：由于是近圆轨道，直接用长半轴估算，以km为单位
            double orbitAltitude = (orbitelem.a - 6378137.0) / 1000;

            //计算轨道周期，以小时为单位
            double orbitPeriod = 2*Math.PI* Math.Sqrt(Math.Pow(orbitelem.a, 3.0) / 3.986005e+14) / 3600;

            //近地点高度
            double Hp = orbitelem.a * (1.0 - orbitelem.e) / 1000.0;

            //远地点高度
            double Ha = orbitelem.a * (1.0 + orbitelem.e) / 1000.0;

            //判断是否为LEO：Ha小于3千公里
            if (Ha < 3000)
            {
                satType = enumSatType.LEO; ;//MEO卫星
                return satType;
            }

            //判断是否是MEO：从3千公里到3万公里高度的卫星都属于MEO
            if (Ha < 30000 && Ha > 3000)
            {
                satType = enumSatType.MEO; ;//MEO卫星
            }
            else
            {
                //判断是否是同步卫星：周期约等于24小时
                if (Math.Abs(orbitPeriod - 24.0) < 0.5)
                {
                    //判断是否是静止卫星：倾角小于5°
                    if (orbitelem.i < 10)
                    {
                        satType = enumSatType.GEO; //GEO卫星
                    }
                    else
                    {
                        satType = enumSatType.IGSO;//IGSO卫星
                    }
                }
                else
                {
                    //前面的都不是，那就应该是HEO：偏心率大于0.1，远地点大于3万公里
                    if (orbitelem.e > 0.1)
                    {
                        satType = enumSatType.HEO;//HEO卫星
                    }
                }
            }

            return satType;
        }

        #endregion 

        #endregion

        public override string ToString()
        {
            return string.Empty;
        }

    }
}
